package com.shareware.common.utils

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.os.Build
import android.text.TextUtils
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.*
import android.view.ViewGroup.MarginLayoutParams
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatDelegate
import com.shareware.common.NEGATIVE_TEN
import com.shareware.common.getContextGlobal
import java.lang.reflect.Method
import kotlin.math.sqrt


/**
 * desc: 功能描述
 * email: mobiledeveloper@qq.com
 */

/**
 * dp转px
 *
 * @param context 上下文
 * @param dpVal   dp
 * @return px
 */
fun dp2px(context: Context, dpVal: Float): Int {
    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP, dpVal, context.resources.displayMetrics
    ).toInt()
}

fun dp2px(dpVal: Float): Int {
    val scale = getContextGlobal().resources.displayMetrics.density
    return (dpVal * scale + 0.5f).toInt()
}

/**
 * sp转px
 *
 * @param context 上下文
 * @param spVal   sp
 * @return px
 */
fun sp2px(context: Context, spVal: Float): Int {
    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_SP, spVal, context.resources.displayMetrics
    ).toInt()
}

fun sp2px(spVal: Float): Int {
    val fontScale = getContextGlobal().resources.displayMetrics.scaledDensity
    return (spVal * fontScale + 0.5f).toInt()
}

/**
 * px转dp
 *
 * @param context 上下文
 * @param pxVal   px
 * @return dp
 */
fun px2dp(context: Context, pxVal: Float): Int {
    val scale = context.resources.displayMetrics.density
    return (pxVal / scale + 0.5f).toInt()
}

fun px2dp(pxVal: Float): Int {
    val scale = getContextGlobal().resources.displayMetrics.density
    return (pxVal / scale + 0.5f).toInt()
}

/**
 * px转sp
 *
 * @param context 上下文
 * @param pxVal   px
 * @return sp
 */
fun px2sp(context: Context, pxVal: Float): Int {
    return (pxVal / context.resources.displayMetrics.scaledDensity + 0.5f).toInt()
}

fun px2sp(pxVal: Float): Int {
    val fontScale = getContextGlobal().resources.displayMetrics.scaledDensity
    return (pxVal / fontScale + 0.5f).toInt()
}

/**
 * 不包括虚拟按键的屏幕宽度
 *
 * @return int
 */
fun getScreenWidth(): Int {
    return getScreenWidth(getContextGlobal())
}

/**
 * 不包括虚拟按键的屏幕宽度
 * @param context 上下文
 * @return int
 */
fun getScreenWidth(context: Context): Int {
    val point = Point()
    (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getSize(
        point
    )
    return point.x
}

/**
 * 包括虚拟按键的屏幕宽度
 */
fun getScreenRealWidth(): Int {
    val point = Point()
    (getContextGlobal().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getRealSize(
        point
    )
    return point.x
}

/**
 * 不包含虚拟导航栏，不包含状态栏
 * @return int
 */
fun getScreenHeight(): Int {
    return getScreenHeight(getContextGlobal())
}


/**
 * 不包含虚拟导航栏，不包含状态栏
 * @param context 上下文
 * @return int
 */
fun getScreenHeight(context: Context): Int {
    val defaultDisplay =
        (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
    val size = Point()
    defaultDisplay.getSize(size)
    val heightPixels = size.y
    val realSize = Point()
    defaultDisplay.getRealSize(realSize)
    val realHeightPixels = realSize.y
    val statusHeight = getStatusHeight(context)
    //虚拟导航栏未展示，某些机型获取的size高度包含状态栏高度
    if (realHeightPixels == heightPixels) {
        return heightPixels - statusHeight
    }
    //虚拟导航栏未展示，某些机型获取的size高度不包含状态栏高度
    if (realHeightPixels - heightPixels == statusHeight) {
        return heightPixels
    }
    //虚拟导航栏展示
    val navBarHeight = getNavigationBarHeight(context)
    if (navBarHeight > 0) {
        val realHeight = realHeightPixels - navBarHeight
        if (realHeight == heightPixels) {
            return realHeight - statusHeight
        }
    }
    return heightPixels
}

/**
 * 包括虚拟导航栏+状态栏+屏幕显示区
 *
 * @return int
 */
fun getScreenRealHeight(): Int {
    val point = Point()
    (getContextGlobal().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getRealSize(
        point
    )
    return point.y
}

/**
 * 判断状态栏是否展示
 */
fun isStatusBarVisible(activity: Activity): Boolean {
    return activity.window.attributes.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN == 0
}

fun getStatusHeight(): Int = getStatusHeight(getContextGlobal())

/**
 * 获得状态栏的高度
 *
 * @param context context
 * @return int
 */
fun getStatusHeight(context: Context): Int {
    val statusBarId = context.resources.getIdentifier("status_bar_height", "dimen", "android")
    return if (statusBarId > 0) {
        context.resources.getDimensionPixelSize(statusBarId)
    } else {
        0
    }
}

fun getNavigationBarHeight(): Int = getNavigationBarHeight(getContextGlobal())

/**
 * 获得底部导航栏的高度
 *
 * @param context context
 * @return int
 */
fun getNavigationBarHeight(context: Context): Int {
    val resources = context.resources
    var narBarHeight = 0
    tryMethodIgnore {
        val resIdNavigationBar =
            resources.getIdentifier("navigation_bar_height", "dimen", "android")
        if (resIdNavigationBar > 0) {//navigationBar高度
            narBarHeight = resources.getDimensionPixelSize(resIdNavigationBar)
        }
    }
    return narBarHeight
}

/**
 * 判断导航栏是否展示
 */
fun isNavBarVisible(): Boolean {
    val wm = getContextGlobal().getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val display = wm.defaultDisplay
    val size = Point()
    val realSize = Point()
    display.getSize(size)
    display.getRealSize(realSize)
    if (isPortrait()) {
        if (size.y == realSize.y) {
            return false
        }
        return realSize.y - size.y != getStatusHeight()
    } else {
        return realSize.x != size.x
    }
}

fun setNavBarColor(window: Window?, @ColorInt color: Int) {
    if (window != null) {
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        window.navigationBarColor = color
    }
}

fun setStatusBarColor(window: Window?, @ColorInt color: Int) {
    if (window != null) {
        //5.0以上必须先清除FLAG_TRANSLUCENT_STATUS才能进行bar绘制
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        //Window负责系统bar的background 绘制
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        window.statusBarColor = color
    }
}

/**
 * 获取当前屏幕截图
 *
 * @param activity activity
 * @return Bitmap
 */
fun snapShot(activity: Activity, statusBarEnable: Boolean = false): Bitmap {
    val decorView = activity.window.decorView
    val bmp = decorView.getCacheBitmap()
    val dm = DisplayMetrics()
    activity.windowManager.defaultDisplay.getMetrics(dm)
    return if (statusBarEnable) {
        Bitmap.createBitmap(bmp, 0, 0, dm.widthPixels, dm.heightPixels)
    } else {
        val statusBarHeight: Int = getStatusHeight(getContextGlobal())
        Bitmap.createBitmap(
            bmp, 0, statusBarHeight, dm.widthPixels, dm.heightPixels - statusBarHeight
        )
    }
}

/**
 * 展示一个屏幕过度动画
 */
fun Activity.showScreenAnimation(duration: Long) {
    val decorView = this.window.decorView
    if (decorView is ViewGroup) {
        val view = View(this)
        view.background = BitmapDrawable(this.resources, decorView.getCacheBitmap())
        val layoutParam = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
        )
        decorView.addView(view, layoutParam)
        val objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
        objectAnimator.duration = duration
        objectAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                decorView.removeView(view)
            }
        })
        objectAnimator.start()
    }
}

private const val TAG_STATUS_BAR_OFFSET_INDEX = NEGATIVE_TEN

/**
 * 设置沉浸式状态栏
 */
@JvmOverloads
fun Activity.setImmersionStatusBar(
    fitsSystemWindows: Boolean = false,
    darkText: Boolean = true,
    needOffsetView: View? = null,
) {
    window?.setBarImmersion(false, null, darkText, fitsSystemWindows, needOffsetView)
}

/**
 * 设置沉浸式状态栏
 * @param statusBarHide false表示view显示延展到状态栏中，true表示隐藏状态栏
 * @param navBarHide null时表示view显示不延展到导航栏，false表示view显示延展到导航栏中，true表示隐藏导航栏
 * @param darkText true 状态栏字体颜色为黑色，系统默认白色
 * statusBarHide和navBarHide都为true时表示全屏
 *
 * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN Activity全屏显示，状态栏遮住Activity,沉浸式会用到
 * SYSTEM_UI_FLAG_FULLSCREEN Activity全屏显示，状态栏被隐藏
 * @param fitsSystemWindows true:默认添加view paddingTop为系统状态栏高度，设置在这个view上的其他padding都会失效
 * android:clipToPadding="false":滚动到padding区域内的视图不会被裁剪，依然显示
 * android:clipChildren="false":view可以超出Parent的视图范围
 */
fun Window.setBarImmersion(
    statusBarHide: Boolean = false,
    navBarHide: Boolean? = null,
    darkText: Boolean = true,
    fitsSystemWindows: Boolean = false,
    needOffsetView: View? = null,
) {
    if (fitsSystemWindows) {
        val contentView = this.findViewById<ViewGroup>(android.R.id.content)
        if (contentView.childCount > 0) {
            val view = contentView.getChildAt(0)
            if (view is ViewGroup) {
                view.fitsSystemWindows = true
            }
        }
    }
    if (needOffsetView != null) {
        val tag = needOffsetView.getTag(TAG_STATUS_BAR_OFFSET_INDEX)
        if (tag != null && tag as Boolean) {
            return
        }
        val layoutParams = needOffsetView.layoutParams as MarginLayoutParams
        layoutParams.setMargins(
            layoutParams.leftMargin,
            layoutParams.topMargin + getStatusHeight(),
            layoutParams.rightMargin,
            layoutParams.bottomMargin
        )
        needOffsetView.setTag(TAG_STATUS_BAR_OFFSET_INDEX, true)
    }
    var uiVisibility = decorView.systemUiVisibility or
            //稳定布局(StatusBar和NavigationBar动态显示或隐藏，fitSystemWindow=true的view设置的padding大小都不会变化
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
            //在不隐藏StatusBar状态栏的情况下，将view显示范围扩展到StatusBar下面
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    if (darkText && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        //状态栏字体颜色为黑色，默认是白色
        uiVisibility = uiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    }
    if (navBarHide != null) {
        //在不隐藏NavBar导航栏的情况下，将view显示范围扩展到系统导航栏的下面
        uiVisibility = uiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        if (navBarHide) {
            //隐藏导航栏
            uiVisibility = uiVisibility or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        }
    }
    if (statusBarHide) {
        uiVisibility = uiVisibility or if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            // 设置窗口占用刘海区
            val lp: WindowManager.LayoutParams = this.attributes
            lp.layoutInDisplayCutoutMode =
                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
            this.attributes = lp
            // 隐藏状态栏
            View.SYSTEM_UI_FLAG_FULLSCREEN
        } else {
            //版本小于28不能设置占用刘海区，加载页布局使用白色背景，达到同样的效果
            //状态栏显示处于低能显示状态(low profile模式)，状态栏上一些图标显示会被隐藏
            View.SYSTEM_UI_FLAG_LOW_PROFILE
        }
    }
    if (statusBarHide || navBarHide != null && navBarHide) {
        //避免某些用户交互造成系统自动清除全屏状态
        uiVisibility = uiVisibility or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    }
    decorView.systemUiVisibility = uiVisibility
    setStatusBarColor(this, Color.TRANSPARENT)
    if (navBarHide != null) {
        setNavBarColor(this, Color.TRANSPARENT)
    }
}

/**
 * 是否是深夜模式
 */
fun isDarkMode(): Boolean {
    return when (AppCompatDelegate.getDefaultNightMode()) {
        AppCompatDelegate.MODE_NIGHT_YES -> true
        AppCompatDelegate.MODE_NIGHT_NO -> false
        AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY, AppCompatDelegate.MODE_NIGHT_UNSPECIFIED -> isDarkModeOnSystem()
        else -> false
    }
}

/**
 * 深夜模式是否跟随系统
 */
fun isDarkModeOnSystem(): Boolean {
    return when (getContextGlobal().resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
        Configuration.UI_MODE_NIGHT_YES -> true
        Configuration.UI_MODE_NIGHT_NO -> false
        else -> false
    }
}

/**
 * 屏幕是否竖屏
 */
fun isPortrait(): Boolean {
    return getContextGlobal().resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
}

/**
 * 屏幕是否横屏
 */
fun isLandscape(): Boolean {
    return getContextGlobal().resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
}

/**
 * 获取屏幕尺寸
 * getRealMetrics包含虚拟导航
 */
fun getScreenInch(): Double {
    val outMetrics = DisplayMetrics()
    (getContextGlobal().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getRealMetrics(
        outMetrics
    )
    return sqrt((outMetrics.widthPixels / outMetrics.xdpi) * (outMetrics.widthPixels / outMetrics.xdpi) + (outMetrics.heightPixels / outMetrics.ydpi) * (outMetrics.heightPixels / outMetrics.ydpi)).toDouble()
        .format(1)
}

fun getScreenRotation(activity: Activity): Int {
    return when (activity.windowManager.defaultDisplay.rotation) {
        Surface.ROTATION_0 -> 0
        Surface.ROTATION_90 -> 90
        Surface.ROTATION_180 -> 180
        Surface.ROTATION_270 -> 270
        else -> 0
    }
}

fun getViewX(view: View): Int {
    val point = IntArray(2)
    view.getLocationOnScreen(point)
    return point[0]
}

fun getViewY(view: View): Int {
    val point = IntArray(2)
    view.getLocationOnScreen(point)
    return point[1]
}

private fun getResNameById(id: Int): String {
    return try {
        getContextGlobal().resources.getResourceEntryName(id)
    } catch (ignore: java.lang.Exception) {
        ""
    }
}

private var notchHeight: Int = -1

/**
 * 获取刘海高度
 * -1 没有刘海屏
 */
fun getNotchHeight(activity: Activity): Int {
    if (!hasNotch(activity)) {
        return -1
    }
    if (notchHeight != -1) {
        return notchHeight
    }
    val height: Int = when {
        isXiaomi() -> {
            getStatusHeight(activity)
        }

        isHuawei() -> {
            getNotchSizeByHuaWei(activity)
        }

        isViVo() -> {
            dp2px(activity, 32f)
        }

        isOppo() -> {
            80
        }

        else -> {
            getNotchSizeByAndroidP(activity)
        }
    }
    notchHeight = height
    return notchHeight
}

private fun getNotchSizeByAndroidP(activity: Activity): Int {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        activity.window?.decorView?.rootWindowInsets?.displayCutout?.safeInsetTop ?: 0
    } else {
        0
    }
}

private fun getNotchSizeByHuaWei(activity: Activity): Int {
    return try {
        val cl: ClassLoader = getContextGlobal().classLoader
        val hwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil")
        val get: Method = hwNotchSizeUtil.getMethod("getNotchSize")
        val ret = get.invoke(hwNotchSizeUtil) as IntArray
        ret[1]
    } catch (e: Exception) {
        getNotchSizeByAndroidP(activity)
    }
}

/**
 * 是否是刘海或是钻孔屏幕 全局只取一次
 */
private var hasNotch: Int = -1

fun hasNotch(activity: Activity): Boolean {
    return if (hasNotch == -1) {
        val result = when {
            isXiaomi() -> {
                hasNotchByXiaoMi()
            }

            isHuawei() -> {
                hasNotchByHuawei()
            }

            isOppo() -> {
                hasNotchByOPPO()
            }

            isViVo() -> {
                hasNotchByViVo()
            }

            isSamsung() -> {
                hasNotchBySamsung()
            }

            else -> {
                hasNotchByOther(activity)
            }
        }
        hasNotch = if (result) {
            1
        } else {
            0
        }
        result
    } else hasNotch != 0
}

private fun hasNotchByXiaoMi(): Boolean {
    return "1" == sysProperty("ro.miui.notch");
}

private fun hasNotchByHuawei(): Boolean {
    return try {
        val cl: ClassLoader = getContextGlobal().classLoader
        val hwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil")
        val get = hwNotchSizeUtil.getMethod("hasNotchInScreen")
        get.invoke(hwNotchSizeUtil) as Boolean
    } catch (e: Exception) {
        false
    }
}

private fun hasNotchByOPPO(): Boolean {
    return getContextGlobal().packageManager.hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}

@SuppressLint("PrivateApi")
private fun hasNotchByViVo(): Boolean {
    var ret = false
    try {
        val classLoader: ClassLoader = getContextGlobal().classLoader
        val feature = classLoader.loadClass("android.util.FtFeature")
        val method = feature.getMethod("isFeatureSupport", Int::class.javaPrimitiveType)
        ret = method.invoke(feature, 0x00000020) as Boolean
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
    }
    return ret
}

private fun hasNotchBySamsung(): Boolean {
    return try {
        val res: Resources = getContextGlobal().resources
        val resId: Int = res.getIdentifier("config_mainBuiltInDisplayCutout", "string", "android")
        val spec: String? = if (resId > 0) res.getString(resId) else null
        spec != null && !TextUtils.isEmpty(spec)
    } catch (e: Exception) {
        false
    }
}

private fun hasNotchByOther(activity: Activity): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        return activity.window?.decorView?.rootWindowInsets?.displayCutout != null
    }
    return false
}

/**
 * 哀悼日置灰
 * 全局置灰可以传入window.decorView
 */
fun setViewGray(view: View) {
    val paint = Paint()
    val cm = ColorMatrix()
    cm.setSaturation(0f)
    paint.colorFilter = ColorMatrixColorFilter(cm)
    view.setLayerType(View.LAYER_TYPE_HARDWARE, paint)
}

fun inRangeOfView(view: View?, ev: MotionEvent): Boolean {
    if (view == null || !view.isShown) {
        return false
    }
    val location = IntArray(2)
    view.getLocationOnScreen(location)
    val x = location[0]
    val y = location[1]
    return !(ev.rawX < x || ev.rawX > x + view.width || ev.rawY < y || ev.rawY > y + view.height)
}